﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace MFramework
{
    [Obsolete]
    public class TimerOld : MonoSingleton<TimerOld>
    {
        private static uint timerID;

        private Dictionary<uint, LinkedListNode<TimerNode>> nodeDic;

        private LinkedList<TimerNode>[][] timeWheel;
        private float startTime;
        private TimeData curTime;

        //（反）注册缓存队列，防止Invoke中触发（反）注册，修改迭代器
        private List<TimerNode> registCache;
        private List<uint> unregistCache;

        protected override void Init()
        {
            registCache = new List<TimerNode>();
            unregistCache = new List<uint>();

            nodeDic = new Dictionary<uint, LinkedListNode<TimerNode>>();
            timeWheel = new LinkedList<TimerNode>[4][];

            timeWheel[0] = new LinkedList<TimerNode>[24];
            timeWheel[1] = new LinkedList<TimerNode>[60];
            timeWheel[2] = new LinkedList<TimerNode>[60];
            timeWheel[3] = new LinkedList<TimerNode>[1000];

            for (int i = 0; i < 4; ++i)
            {
                for (int j = 0; j < timeWheel[i].Length; ++j)
                {
                    timeWheel[i][j] = new LinkedList<TimerNode>();
                }
            }

            startTime = Time.time;
            lastMS = 0;
        }

        /* interval: default zero, represents only invoke once
         * duration: default negative, represents loopendless
         */
        public uint RegistTimer(Action action, uint delay, uint interval = 0, int duration = -1, Action onSuccessEnd = null)
        {
            /*
             * 由于存在缓存队列，在定时任务中注册delay为0的定时任务ms指针旋转一周后才会触发，
             * 且会导致InvokeTime落后实际事时间，导致重复事件在下一天才会触发
             */
            if (delay == 0) delay = 1;

            TimerNode tNode = new TimerNode
            {
                timerID = timerID++,
                action = action,
                invokeTime = NumberToTime(TimeToNumber(curTime) + delay),
                interval = interval,
                onSuccessEnd = onSuccessEnd
            };

            if (duration < 0 && interval != 0)
                tNode.loopEndless = true;
            else
                tNode.loopTime = (interval == 0 ? 1 : (uint)(duration / interval + 1));

            registCache.Add(tNode);

            return tNode.timerID;
        }

        public void UnRegistTimer(uint timerID)
        {
            unregistCache.Add(timerID);
        }

        private void __RegistTimer()
        {
            if (registCache.Count == 0) return;

            foreach (TimerNode tNode in registCache)
            {
                InsertTimer(tNode);
            }

            registCache.Clear();
        }

        private void __UnRegistTimer()
        {
            if (unregistCache.Count == 0) return;

            foreach (uint timerID in unregistCache)
            {
                if (nodeDic.ContainsKey(timerID))
                {
                    LinkedListNode<TimerNode> llNode = nodeDic[timerID];
                    TimerNode tnode = llNode.Value;

                    timeWheel[tnode.wheelIndex][tnode.slot].Remove(llNode);

                    nodeDic.Remove(timerID);
                }
            }

            unregistCache.Clear();
        }

        private ulong lastMS;

        private void Update()
        {
            ulong nowMS = (ulong)((Time.time - startTime) * 1000);
            ulong delta = nowMS - lastMS;
            lastMS = nowMS;

            while (delta-- != 0)
            {
                MoveStep();
            }
        }

        private void MoveStep()
        {
            __RegistTimer();
            __UnRegistTimer();

            //处理当前指针指向的槽事件
            Process(3);

            if (++curTime[3] == 1000)
            {
                curTime[3] = 0;

                if (++curTime[2] == 60)
                {
                    curTime[2] = 0;

                    if (++curTime[1] == 60)
                    {
                        curTime[1] = 0;

                        if (++curTime[0] == 24)
                        {
                            curTime[0] = 0;
                        }
                        Process(0);
                    }
                    Process(1);
                }
                Process(2);
            }
        }

        private void Process(int index)
        {
            foreach (TimerNode tNode in timeWheel[index][curTime[index]])
            {
                if (index == 3)
                {
                    InvokeTimer(tNode);
                }
                else
                {
                    InsertTimer(tNode);
                }
            }

            timeWheel[index][curTime[index]].Clear();
        }

        private void InsertTimer(TimerNode tNode)
        {
            for (int i = 0; i < 4; ++i)
            {
                if (tNode.invokeTime[i] != curTime[i] || i == 3)
                {
                    tNode.wheelIndex = (uint)i;
                    tNode.slot = tNode.invokeTime[i];

                    var lnode = timeWheel[i][tNode.invokeTime[i]].AddLast(tNode);

                    nodeDic[tNode.timerID] = lnode;
                    break;
                }
            }
        }

        private void InvokeTimer(TimerNode tNode)
        {
            tNode.action?.Invoke();
            --tNode.loopTime;

            if (tNode.loopTime > 0 || tNode.loopEndless)
            {
                tNode.invokeTime = NumberToTime(TimeToNumber(tNode.invokeTime) + tNode.interval);

                InsertTimer(tNode);
            }
            else
            {
                tNode.onSuccessEnd?.Invoke();
                nodeDic.Remove(tNode.timerID);
            }
        }

        private uint TimeToNumber(TimeData time)
        {
            return time[3] + (time[2] + (time[1] + time[0] * 60) * 60) * 1000;
        }

        private TimeData NumberToTime(uint ms)
        {
            TimeData time = new TimeData();

            time[3] = ms % 1000; ms /= 1000;
            time[2] = ms % 60; ms /= 60;
            time[1] = ms % 60; ms /= 60;
            time[0] = ms % 24;

            return time;
        }

        struct TimerNode
        {
            public uint timerID;
            public Action action;
            public Action onSuccessEnd;
            public TimeData invokeTime;
            public uint interval;
            public uint loopTime;
            public bool loopEndless;
            //location
            public uint wheelIndex;
            public uint slot;
        }

        struct TimeData
        {
            public uint hour;
            public uint minute;
            public uint second;
            public uint millisecond;

            public uint this[int i]
            {
                get
                {
                    switch (i)
                    {
                        case 0: return hour;
                        case 1: return minute;
                        case 2: return second;
                        case 3: return millisecond;
                    }
                    throw new Exception("Index out of range");
                }
                set
                {
                    switch (i)
                    {
                        case 0: hour = value; return;
                        case 1: minute = value; return;
                        case 2: second = value; return;
                        case 3: millisecond = value; return;
                    }
                    throw new Exception("Index out of range");
                }
            }

            public override string ToString()
            {
                return String.Format("{0}, {1}, {2}, {3}", hour, minute, second, millisecond);
            }
        }
    }
}